/*
**
**			Heroes IV
**	  Copyright 2001, The 3DO Company
**
**      dialog_marketplace.cpp
*/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "dialog_marketplace.h"

#include "abbreviate_number.h"
#include "adaptor_handler.h"
#include "adventure_frame.h"
#include "adventure_map.h"
#include "army.h"
#include "basic_dialog.h"
#include "bitmap_group.h"
#include "bitmap_group_ptr.h"
#include "bitmap_group_cache.h"
#include "bitmap_layer.h"
#include "bitmap_layer_window.h"
#include "bound_handler.h"
#include "button.h"
#include "button_cache.h"
#include "elements_of.h"
#include "external_string.h"
#include "format_string.h"
#include "game_replace_keywords.h"
#include "interface_help.h"
#include "material_names.h"
#include "player.h"
#include "rational.h"
#include "replace_keywords.h"
#include "scrollbar.h"
#include "standard_fonts.h"


extern	t_external_string const k_hotkey_buy;
extern  t_button_cache const    k_buy_button;
extern  t_button_cache			g_ok_button;
extern t_bitmap_group_cache const   k_large_material_bitmaps;
extern t_button_cache       g_max_button;

t_bitmap_group_cache k_marketplace_bitmaps( "dialog.marketplace" );

static t_button* create_marketplace_player_button ( t_screen_point &point, t_window *parent_window, int material_type,
                std::string keyword, t_help_block const& help_block )
{
	t_button*		        button;
	t_window*               window;
	t_screen_point          origin(0,0);
	t_bitmap_layer const*   layer;
	t_bitmap_group_ptr      material_bitmaps_ptr = k_large_material_bitmaps.get();
    
	button = new t_button( point, parent_window );
    layer = material_bitmaps_ptr->find ( k_material_keyword[ material_type ] );
    window = new t_bitmap_layer_window( layer, origin, button );
	set_help( window, help_block, keyword );

	button->set_released_image( window );
	button->set_pressed_image( window );

	button->update_size();

	return button;
}

t_dialog_marketplace::t_dialog_marketplace ( t_window *parent)
					: t_window( k_completely_transparent, parent )
{
	m_market_selected = false;
	m_player_selected = false;
	m_market_item_window = NULL;
	m_player_item_window = NULL;
    
    m_player_item_index = -1;
    m_market_item_index = -1;
    
    m_player_quantity = 0;
}

// Pass in ratio... (for purchases)
void t_dialog_marketplace::init_dialog( t_window* parent, t_adventure_frame* frame, std::string const& name_text, std::string const& intro_text, 
                int market_efficiency_ratio )
{

	t_bitmap_layer const*   layer;
	//t_window *              window;
	t_window *              background_window;
	t_screen_point          origin(0,0);
	t_screen_point          point(0,0);
	t_screen_rect           rect;
	t_bitmap_group_ptr      material_bitmaps_ptr = k_large_material_bitmaps.get();
    int                     i;
	t_help_block const&     material_help = get_help_block( "material_display" );
	t_help_block const&		help = get_help_block( "marketplace" );

	m_player_ptr = &frame->get_map()->get_player();
	m_player_funds = m_player_ptr->get_funds()[k_gold];
	m_bitmaps_ptr = k_marketplace_bitmaps.get();
    m_market_efficiency_ratio = market_efficiency_ratio;

	layer = m_bitmaps_ptr->find( "background" );
	background_window = new t_bitmap_layer_window( layer, origin, this );

	layer = m_bitmaps_ptr->find( "frame" );
   	rect = layer->get_rect();
	m_player_frame = new t_bitmap_layer_window( layer, -rect.top_left(), this );
	m_player_frame->set_visible( false );
	m_player_frame->update_size();

	layer = m_bitmaps_ptr->find( "frame" );
   	rect = layer->get_rect();
	m_market_frame = new t_bitmap_layer_window( layer, -rect.top_left(), this );
	m_market_frame->set_visible( false );
	m_market_frame->update_size();

    // Find wood frame (first item) - get diff for frame.
	t_screen_point  frame_diff(0,0);

    point = m_bitmaps_ptr->find( "wood_icon" )->get_rect().top_left();
    frame_diff = (point - rect.top_left());

    /*
    ** Materials:
    */
	std::string     material_icon_text;

	for (i=0; i<k_material_count; i++)
    {
        // Player's material.
		material_icon_text = format_string ( "%s_icon", k_material_keyword[i] );
	    rect = m_bitmaps_ptr->find( material_icon_text )->get_rect();
        m_player_button[i] = create_marketplace_player_button ( rect.top_left(), background_window, i,
                        k_material_keyword[i], help );
	
        m_player_button[i]->set_click_handler( add_2nd_argument( bound_handler( *this, &t_dialog_marketplace::player_material_clicked ), i));
	    m_player_frame_locations.push_back ( rect.top_left() - frame_diff );

        // Market material.
		material_icon_text = format_string ( "%s_icon_2", k_material_keyword[i] );
	    rect = m_bitmaps_ptr->find( material_icon_text )->get_rect();
        m_market_button[i] = create_marketplace_player_button ( rect.top_left(), background_window, i,
                        format_string( "%s_%s", "available", k_material_keyword[i] ), help );
	
        m_market_button[i]->set_click_handler( add_2nd_argument( bound_handler( *this, &t_dialog_marketplace::market_material_clicked ), i));
	    m_market_frame_locations.push_back ( rect.top_left() - frame_diff );
    }

    /*
    ** Create buttons.
    */
	t_button            *button_ptr;
	t_button_handler    button_handler;

	rect = m_bitmaps_ptr->find( "close_button" )->get_rect();
	button_ptr = new t_button( g_ok_button.get(), rect.top_left(), this );
	button_ptr->set_click_handler( bound_handler( *this, &t_dialog_marketplace::close_click ));
	set_help( button_ptr, help, "ok" );

    /*
    ** Add buy button.
    */
	point = m_bitmaps_ptr->find( "sell_button" )->get_rect().top_left() + origin;
	m_buy_button = new t_button( k_buy_button.get(), point, this );
	button_handler = bound_handler( *this, &t_dialog_marketplace::buy_clicked );
	m_buy_button->set_click_handler( button_handler );
	set_help( m_buy_button, help, "purchase" );
	m_buy_button->enable( false );

    // Add max button.
	point = m_bitmaps_ptr->find( "max_button" )->get_rect().top_left() + origin;
	m_max_button = new t_button( g_max_button.get(), point, this );
	button_handler = bound_handler( *this, &t_dialog_marketplace::max_clicked );
	m_max_button->set_click_handler( button_handler );
	set_help( m_max_button, help, "maximum" );
	m_max_button->enable( false );

    /*
    ** Add scrollbar.
    */
	layer = m_bitmaps_ptr->find( "scrollbar" );
	rect  = layer->get_rect();

	m_scrollbar = new t_scrollbar( rect.top_left(), rect.width(), this, 0, 0, false );
	m_scrollbar->set_handler( bound_handler( *this, &t_dialog_marketplace::scrollbar_move));
	m_scrollbar->set_position( 0 );
	m_scrollbar->set_limits( 0, 0 );

    /*
    ** Text windows.
    */
	t_text_window* text_window_ptr;

    // Title.
	rect = m_bitmaps_ptr->find( "title" )->get_rect() + origin;
	text_window_ptr = new t_text_window( get_font( 20 ), rect, this, name_text, t_pixel_24(0,0,0));
	text_window_ptr->set_center_horizontal();
	text_window_ptr->set_drop_shadow( false );

    // Intro text.
	rect = m_bitmaps_ptr->find( "text" )->get_rect() + origin;
	text_window_ptr = new t_text_window( get_font( 20 ), rect, this, intro_text, t_pixel_24(0,0,0));
	text_window_ptr->set_center_horizontal();
	text_window_ptr->set_drop_shadow( false );

	std::string     material_text_box;
    
	for (i=0; i<k_material_count; i++)
    {
        // Player's side.  Kindgom amount.
		material_text_box = format_string ( "kingdom_%s", k_material_keyword[i] );
	    rect = m_bitmaps_ptr->find( material_text_box )->get_rect();
	    m_player_amount_text[i] = new t_text_window( get_font( 16 ), rect, this, "", t_pixel_24(0,0,0));
	    m_player_amount_text[i]->set_center_horizontal();
        
        // Market exchange text.
		material_text_box = format_string ( "%s_exchange", k_material_keyword[i] );
	    rect = m_bitmaps_ptr->find( material_text_box )->get_rect();
	    m_market_exchange_text[i] = new t_text_window( get_font( 16 ), rect, this, "", t_pixel_24(0,0,0));
	    m_market_exchange_text[i]->set_center_horizontal();

    }

    show_player_material_amount();

    // Player quantity text.
	rect = m_bitmaps_ptr->find( "qty_selling" )->get_rect() + origin;
	m_player_quantity_text = new t_text_window( get_font( rect.height() ), rect, this, "", t_pixel_24(0,0,0));
	m_player_quantity_text->set_center_horizontal();
	m_player_quantity_text->set_drop_shadow( false );

    // Market quantity text.
	rect = m_bitmaps_ptr->find( "exchange_rate" )->get_rect() + origin;
	m_market_quantity_text = new t_text_window( get_font( rect.height() ), rect, this, "", t_pixel_24(0,0,0));
	m_market_quantity_text->set_center_horizontal();
	m_market_quantity_text->set_drop_shadow( false );

    show_exchange_rates();

    /*
    ** Center the position and display the window.
    */
    t_screen_rect parent_rect = get_parent()->get_client_rect();
    
    rect = m_bitmaps_ptr->get_rect();
    rect += (parent_rect.size() - rect.size()) / 2;
    init( rect );

}

static void set_text( t_text_window* window, int amount )
{
	t_font const&	font = *window->get_font().get();
	int				width = window->get_width();

	window->set_text( abbreviate_number( amount, font, width ) );
}

void t_dialog_marketplace::show_player_material_amount()
{
	t_material_array const& funds = m_player_ptr->get_funds();
    int                     i;

	for (i=0; i<k_material_count; i++)
    {
        // Player's side.  Kindgom amount.
		set_text( m_player_amount_text[i], funds[i] );
    }
}

// Clicked on a material on the player's side.
void t_dialog_marketplace::player_material_clicked( t_button* button, int index )
{
    if (m_player_item_index == index)
        return;

	m_player_selected = true;
    m_player_item_index = index;

    // Set frame.
   	m_player_frame->move( m_player_frame_locations[index] );
	m_player_frame->set_visible( true );

    // Reset quantity.
    m_player_quantity = 0;
    m_market_quantity = 0;

	m_scrollbar->set_position( 0 );

    show_exchange_items();
    show_exchange_rates();
	show_player_material_amount();
}

// Clicked on a material on the market side.
void t_dialog_marketplace::market_material_clicked( t_button* button, int index )
{
	t_screen_rect           rect;

    if (m_market_item_index == index)
        return;

	m_market_selected = true;
    m_market_item_index = index;

    // Set frame.
   	m_market_frame->move( m_market_frame_locations[index] );
    m_market_frame->set_visible( true );

    // Reset quantity.
    m_player_quantity = 0;
    m_market_quantity = 0;
    
	m_scrollbar->set_position( 0 );

    show_exchange_items();
    show_exchange_rates();
	show_player_material_amount();
}

// Show selected items
void t_dialog_marketplace::show_exchange_items()
{
	t_bitmap_group_ptr      material_bitmaps_ptr = k_large_material_bitmaps.get();
	t_screen_rect           rect;
	t_bitmap_layer const*   layer;

    // Close previous items, if any.
	if (m_player_item_window)
    {
        m_player_item_window->close();
        m_player_item_window = NULL;
    }
    
	if (m_market_item_window)
    {
        m_market_item_window->close();
        m_market_item_window = NULL;
    }

    // Show seller's item icon.
	if ( m_player_selected )
	{
		rect = m_bitmaps_ptr->find( "selling_icon" )->get_rect();
		layer = material_bitmaps_ptr->find ( k_material_keyword[ m_player_item_index ] );
		m_player_item_window = new t_bitmap_layer_window( layer, rect.top_left(), this );
		set_text( m_player_quantity_text, m_player_quantity);
	}

    // Show market's item icon.
	if ( m_market_selected )
	{
		rect = m_bitmaps_ptr->find( "buying_icon" )->get_rect();
		layer = material_bitmaps_ptr->find ( k_material_keyword[ m_market_item_index ] );
		m_market_item_window = new t_bitmap_layer_window( layer, rect.top_left(), this );
		set_text( m_market_quantity_text, m_market_quantity );
	}

    // Not applicable if buying and selling item are the same.
    if ( can_buy() )
    {
		// Enable the buy buttons
		m_buy_button->enable( true );
		m_max_button->enable( true );
    }
	else
	{
        // Don't display quantity.
        m_player_quantity_text->set_text ( format_string( "" ) );
        m_market_quantity_text->set_text ( format_string( "" ) );

        // Reset scroll bar.
		m_max_scroll_value = 0;
	    m_scrollbar->set_position( 0 );
        m_scrollbar->set_limits( 0, 0 );

		// Disable the buy buttons
		m_buy_button->enable( false );
		m_max_button->enable( false );
	}
}

static t_external_string const k_text_dialog_marketplace_not_applicable( "not_applicable.misc" );

/*
** Calculate and show the exchange rates.
*/
void t_dialog_marketplace::show_exchange_rates()
{
    int i;
    int player_item_cost;
    int numerator;
    int denominator;
    int gcd;
	t_material_array const& funds = m_player_ptr->get_funds();

    if (m_player_selected == false)
        return;

    // Player:market ratio.
    player_item_cost = k_material_value[m_player_item_index];

    /*
    ** Calculate ratios for each market item and display it.
    */
	for (i=0; i<k_material_count; i++)
    {
        // Calculate exchange rate.  Market ratio number * material cost.
        denominator = m_market_efficiency_ratio * k_material_value[i];

        // Find greatest common denominator.
        gcd = greatest_common_divisor ( player_item_cost, denominator );

        // Find ratio  numerator:denominator
        numerator = player_item_cost/gcd;
        denominator /= gcd;
                
        // Market exchange text.  
        if (i == m_player_item_index)
        {
            // Same item, not applicable.
    	    m_market_exchange_text[i]->set_text( k_text_dialog_marketplace_not_applicable );
        }
        else if (i == k_gold)
        {
            // For gold we don't want a ratio, just a price per unit (since the ratios tend to be very high)
			numerator /= denominator;
			denominator = 1;
    	    m_market_exchange_text[i]->set_text( format_string( "%d", numerator ));
        }
        else
        {
            // Show ratio.
    	    m_market_exchange_text[i]->set_text( format_string( "%d / %d", numerator, denominator ) );
        }

        // For the market item, calculate the maximum that can be purchased.
        if (i == m_market_item_index && can_buy())
        {
            m_cost_numerator = numerator;
            m_cost_denominator = denominator;

            m_max_scroll_value = funds[m_player_item_index] / denominator;
	        m_scrollbar->set_limits( 0, m_max_scroll_value );
        }
    }
}

void t_dialog_marketplace::reset_market_rate_text()
{
    int i;

	for (i=0; i<k_material_count; i++)
    {
   	    m_market_exchange_text[i]->set_text( "" );
    }
}

// Moved scroll bar.
void t_dialog_marketplace::scrollbar_move( t_scrollbar* scrollbar, int value )
{
    if ((m_player_selected == false) || (m_market_selected == false))
        return;

	m_player_quantity = value * m_cost_denominator;	// Undo the 1/denominator used to get the scrollbar to move in units of denominator
	set_text( m_player_quantity_text, m_player_quantity );

	m_market_quantity = value * m_cost_numerator;	// 1/denominator is already built into value
	set_text( m_market_quantity_text, m_market_quantity );

	// Update the players resource amount to reflect how much will be spent and purchased
	if (m_player_item_index >= 0 && m_player_item_index < k_material_count) 
	{
		t_material_array const& funds = m_player_ptr->get_funds();

		set_text( m_player_amount_text[m_player_item_index], 
			      funds[m_player_item_index] - m_player_quantity );
		set_text( m_player_amount_text[m_market_item_index],
				  funds[m_market_item_index] + m_market_quantity );
	}
}

void t_dialog_marketplace::buy_clicked( t_button* button )
{
	if ((m_player_quantity == 0) || (m_market_quantity == 0))
        return;

    // Purchase exchange.
	m_player_ptr->spend( (t_material)m_player_item_index, m_player_quantity );
	m_player_ptr->gain( (t_material)m_market_item_index, m_market_quantity );

    // Reset screen - sequence is important.
    m_player_item_index = -1;
    m_market_item_index = -1;

	m_player_selected = false;
	m_market_selected = false;

	m_player_frame->set_visible( false );
	m_market_frame->set_visible( false );

    reset_market_rate_text();
    show_exchange_items();
    show_player_material_amount();
}

void t_dialog_marketplace::max_clicked( t_button* button )
{
	m_scrollbar->set_position( m_max_scroll_value );
}

void t_dialog_marketplace::close_click( t_button* button )
{
	m_return_value = false;
	close();
}

